package com.director.core;

import com.director.core.annotation.DirectReturnIncludeStrategy;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Simone Ricciardi
 * @version 1.0, 10/27/2011
 */
public class IncludeReturnDataStrategy implements ReturnDataStrategy {

   private DirectReturnIncludeStrategy strategy;

   private Map<Object, List<String>> serializationMap = new HashMap<Object, List<String>>();

   public IncludeReturnDataStrategy(DirectReturnIncludeStrategy strategy) {
      this.strategy = strategy;
   }

   @Override
   public Map<Object, List<String>> buildSerializationMap(Object result) {
      for(String fieldPattern : this.strategy.fieldPatterns()) {
         try {
            this.buildRecursively(result, fieldPattern);
         } catch(Exception e) {
            throw new DirectException(e);
         }
      }
      return this.serializationMap;
   }

   private void buildRecursively(Object obj, String fieldPattern) throws Exception {
      String fieldToInclude = fieldPattern;
      if(fieldPattern.contains(".")) {
         fieldToInclude = fieldPattern.substring(0, fieldPattern.indexOf('.'));
         String nextLevelPattern = fieldPattern.substring(fieldPattern.indexOf('.')+1);
         Field field = obj.getClass().getDeclaredField(fieldToInclude);
         field.setAccessible(true);
         Object fieldValue = field.get(obj);
         if(Collection.class.isAssignableFrom(fieldValue.getClass())) {
            for(Object item : ((Collection) fieldValue)) {
               this.buildRecursively(item, nextLevelPattern);
            }
         } else {
            this.buildRecursively(fieldValue, nextLevelPattern);
         }
      }

      this.getFieldListFor(obj).add(fieldToInclude);
   }

   private List<String> getFieldListFor(Object o) {
      List<String> fields = this.serializationMap.get(o);
      if(fields == null) {
         fields = new ArrayList<String>();
         this.serializationMap.put(o, fields);
      }
      return fields;
   }
}
